from django.db import models
from django.contrib.auth.models import User

import movie.vote.weighting as weighting


class Movie(models.Model):
  name = models.CharField(max_length=255, primary_key=True) 
  date_added = models.DateField()
  imdb_url = models.URLField(blank=True)
  rotten_url = models.URLField(blank=True)
  metacritic_url = models.URLField(blank=True)
  youtube_trailer_url = models.URLField(blank=True)
  voters = models.ManyToManyField(User, related_name='voted_movies', blank=True)
  downvoters = models.ManyToManyField(User, related_name='downvoted_movies', blank=True)
  tally = models.FloatField(default=0)
  tally_noweight = models.IntegerField(default=0)

  rotten_score = models.IntegerField(null=True, blank=True)
  rotten_score_last_updated = models.DateField(null=True, blank=True)
  metacritic_score = models.IntegerField(null=True, blank=True)
  metacritic_score_last_updated = models.DateField(null=True, blank=True)
  review_average_score = models.FloatField(null=True, blank=True)

  # Whether the movie can be voted on
  votable = models.BooleanField(default=True)

  # The voting algorithm to use
  VOTING_ALGORITHM = weighting.Egalitarian

  def __str__(self):
    return self.name

  @classmethod
  def get_weight(cls, user):
    return cls.VOTING_ALGORITHM().get_weight(user)

  def update_tally(self, weights=None):
    """weights can be a dictionary of usernames to weights."""
    tally = 0.0
    tally_noweight = 0
    for user in self.voters.all():
      if weights:
        tally += weights[user.username]
      else:
        tally += self.get_weight(user)
      tally_noweight += 1
    for user in self.downvoters.all():
      if weights:
        tally -= weights[user.username]
      else:
        tally -= self.get_weight(user)
      tally_noweight -= 1
    screenings = self.screening_set.count()
    tally -= screenings
    tally_noweight -= screenings

    # Take care of tallies that are very close to 0
    if round(tally, 2) == 0:
      tally = 0.0

    need_save = False
    if self.tally != tally:
      self.tally = tally
      need_save = True
    if self.tally_noweight != tally_noweight:
      self.tally_noweight = tally_noweight
      need_save = True
    if need_save:
      self.save()

  @classmethod
  def update_all_tallies(cls):
    # Build a dictionary of weights for caching
    weights = {}
    for user in User.objects.all():
      weights[user.username] = cls.get_weight(user)
    for movie in cls.objects.all():
      movie.update_tally(weights=weights)


class Screening(models.Model):
  movie = models.ForeignKey(Movie)
  date = models.DateField()
  attendees = models.ManyToManyField(User, related_name='attended_screenings', blank=True)

  def __str__(self):
    return self.movie.name
